package jtech;

import static javax.sound.sampled.AudioFormat.Encoding.PCM_SIGNED;

import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;

import javax.sound.sampled.AudioFormat;
import javax.swing.JFrame;

import jtech.core.AwtSystem;
import jtech.core.Loader;
import jtech.core.SoundSystem;

public abstract class JTech implements Inputs {

    private static AudioFormat playbackFormat = new AudioFormat(PCM_SIGNED, 44100, 16, 2, 4, 44100, false);
	
	private static SoundSystem soundSystem = SoundSystem.getInstance();
	private static AwtSystem awtSystem = AwtSystem.getInstance();
	private static Loader loader = Loader.getInstance();
	private static Drawer drawer = Drawer.getInstance();
	
	private int frameRate;
	private boolean running;
	private int fps;
	private int ups;
	
    public JTech() {
        
    }
    
    public void startGame() {

    	try {
	    	soundSystem.init(playbackFormat, 32);
			awtSystem.init();
	        
	    	running = true;
	        frameRate = 60;
	        
	        start();
	        
    	} finally {
    		soundSystem.destroy();
			awtSystem.destroy();
    			
	        exit();
    	}
    }
	
	public void stopGame() {
	    running = false;
	}
    
	protected void init() {
	    
	}
	
	protected void shutdown() {
	    
	}
	
	protected void update(float elapsedTime) {
	    
	}
	
	protected void draw(Graphics2D g2d) {
	    
	}
	
    public int getFrameRate() {
        return frameRate;
    }

    public void setFrameRate(int frameRate) {
        this.frameRate = frameRate;
    }

    public int getFps() {
        return fps;
    }
    
    public int getUps() {
        return ups;
    }

	private void start() {
	    int frames = 0, updates = 0;
	    double unprocessed = 0;
	    float lastTime, lastRecord;

	    init();
        
        lastRecord = lastTime = time();
        
        while (running) {

            final float dt = 1f / frameRate;
            final float now = time();
            
            unprocessed += (now - lastTime);

            awtSystem.updateMousePosition();
            
            if (now - lastRecord >= 1f) {
                lastRecord = now;
                fps = frames;
                ups = updates;
                frames = updates= 0;
            }
            
            boolean shouldRender = false;
            while (unprocessed >= dt) {
                shouldRender = true;
                unprocessed -= dt;
                update(dt);
                updates++;
                awtSystem.updateInputState();
            }

            BufferStrategy strategy = getWindow().getBufferStrategy();
            if (shouldRender && strategy != null) {
                Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();

                drawer.create(g2d);
                draw(g2d);
                drawer.disposeAll();

                strategy.show();
                frames++;
                
                Toolkit.getDefaultToolkit().sync();
                
            }
            
            lastTime = now;
            
            sleep(.001f);
        }

        shutdown();
       
	}
    
	public static void sleep(float secs) {
		awtSystem.sleep(secs);
	}
	
	private void exit() {
		awtSystem.exit();
    }
	
    public static float time() {
    	return awtSystem.time();
    }
    
    public JFrame getWindow() {
    	return awtSystem.getWindow();
    }
    
    public static void loadImage(String name, String path) {
    	loader.loadImage(name, path);
    }
    
    public static void loadSound(String name, String path) {
    	loader.loadSound(name, path, playbackFormat);
    }
    
    public static void playSound(String name, boolean blocking) {
		soundSystem.play(name, blocking);
	}
    
    public boolean isKeyPressed(int key) {
    	return awtSystem.isKeyPressed(key);
    }
    
    public boolean isKeyDown(int key) {
    	return awtSystem.isKeyDown(key);
    }
    
    public boolean isKeyReleased(int key) {
    	return awtSystem.isKeyReleased(key);
    }
    
    public boolean isButtonDown(int button) {
    	return awtSystem.isButtonDown(button);
    }
    
    public boolean isButtonPressed(int button) {
    	return awtSystem.isButtonPressed(button);
    }
    
    public boolean isButtonReleased(int button) {
    	return awtSystem.isButtonReleased(button);
    }

    public boolean isRelativeMouseMode() {
		return awtSystem.isRelativeMouseMode();
	}

	public void setRelativeMouseMode(boolean relativeMouseMode) {
		awtSystem.setRelativeMouseMode(relativeMouseMode);
	}

	public void setMouseVisible(boolean visible) {
		awtSystem.setMouseVisible(visible);
	}

	public boolean isMouseVisible() {
		return awtSystem.isMouseVisible();
	}

	public int getMouseX() {
		return awtSystem.getMouseX();
	}

	public int getMouseY() {
		return awtSystem.getMouseY();
	}

	public int getMouseWheel() {
		return awtSystem.getMouseWheel();
	}

	public Point getWindowCenter() {
		return awtSystem.getWindowCenter();
	}

	public Dimension getWindowSize() {
		return awtSystem.getWindowSize();
	}

	public DisplayMode getDisplayMode() {
		return awtSystem.getDisplayMode();
	}

	public boolean isFullScreen() {
		return awtSystem.isFullScreen();
	}

	public void setFullScreenMode(DisplayMode displayMode) {
		awtSystem.setFullScreenMode(displayMode);
	}

	public void setWindowMode(Dimension windowSize) {
		awtSystem.setWindowMode(windowSize);
	}

	public void setWindowMode(int w, int h) {
		awtSystem.setWindowMode(w, h);
	}
	
	public int getScreenWidth() {
		return isFullScreen() ? getDisplayMode().getWidth() : getWindowSize().width;
	}
	
	public int getScreenHeight() {
		return isFullScreen() ? getDisplayMode().getHeight() : getWindowSize().height;
	}
	
	public static Drawer getDrawer() {
		return drawer;
	}
	
}
